package org.helgoboss.janana_tray_resource_management

import scala.swing._
import scala.swing.event._
import org.osgi.service.obr.Repository
import javax.swing.event.{DocumentListener, DocumentEvent, TableModelListener, TableModelEvent}
import javax.swing.UIManager
import javax.swing.table.TableModel
import org.helgoboss.scala_swing_additions.{MigPanel, ProgressView}
import org.osgi.framework.{Bundle, Version}
import java.awt.Toolkit
import org.helgoboss.janana_artwork.JananaLogo

abstract class BundleResourceManagementView {
    private lazy val bundleResourceTable: Table = {
        val table = new Table {
            focusable = true
            
            listenTo(selection, keys)
            
            reactions += {
                case KeyPressed(_, Key.I, _, _) =>
                    installSelectedBundleResource
                    
                case KeyPressed(_, Key.U, _, _) =>
                    uninstallSelectedBundleResource
                    
                case KeyPressed(_, Key.R, _, _) =>
                    refreshSelectedBundleResource
                    
                case KeyPressed(_, Key.S, _, _) =>
                    bundleResourceTextField.requestFocus
                    
                case TableRowsSelected(_, _, _) =>
                    updateButtons
            }
            
            model = bundleResourceTableModel
        }
        /* Set column widths */
        val columnModel = table.peer.getColumnModel
        columnModel.getColumn(0).setPreferredWidth(300)
        columnModel.getColumn(1).setPreferredWidth(100)
        columnModel.getColumn(2).setPreferredWidth(100)
        columnModel.getColumn(3).setPreferredWidth(200)
        columnModel.getColumn(4).setMaxWidth(100)
        
        table
    }
    
    object bundleResourceTableModel extends TableModel {
        private val listeners = new collection.mutable.HashSet[TableModelListener]
        
        def updateData {
            listeners.foreach(_.tableChanged(new TableModelEvent(this)))
        }
        
        def addTableModelListener(listener: TableModelListener) {
            listeners += listener
        }
        
        def removeTableModelListener(listener: TableModelListener) {
            listeners -= listener
        }
        
        def getColumnClass(columnIndex: Int) = {    
            columnIndex match {
                case x if 0 until 4 contains x => classOf[String]
                case 5 => classOf[Boolean]
            }
        }
        
        def getColumnCount = 6
        
        def getColumnName(columnIndex: Int) = {
            columnIndex match {
                case 0 => "Symbolic Name"
                case 1 => "Version"
                case 2 => "Categories"
                case 3 => "Presentation Name"
                case 4 => "State"
                case 5 => "Repository"
            }
        }
        
        def getRowCount = bundleResources.size
        
        def getValueAt(rowIndex: Int, columnIndex: Int) = {
            val bundleResource = bundleResources(rowIndex)
            columnIndex match {
                case 0 => bundleResource.symbolicName
                case 1 => bundleResource.version.toString
                case 2 => bundleResource.categories.mkString(", ")
                case 3 => bundleResource.presentationName
                case 4 => bundleResource.state.displayName
                case 5 => 
                    bundleResource.resource match {
                        case Some(resource) => 
                            /* TODO Activate as soon as getRepository method is implemented */
                            //~ resource.getRepository.getURL.toString
                            "Unknown"
                        case None => "Not available"
                    }
            }
        }
        
        def isCellEditable(rowIndex: Int, columnIndex: Int) = false
        
        def setValueAt(value: AnyRef, rowIndex: Int, columnIndex: Int) {
        }
    }
    
    private lazy val bundleResourceTextField: TextField = new TextField(50) {
        focusable = true
        peer.getDocument addDocumentListener new DocumentListener {
            def changedUpdate(e: DocumentEvent) {
                updateBundleResourceTable
            }
            def insertUpdate(e: DocumentEvent) {
                updateBundleResourceTable
            }
            def removeUpdate(e: DocumentEvent) {
                updateBundleResourceTable
            }
        }
        listenTo(keys)
        reactions += {
            case KeyPressed(_, Key.Up, _, _) =>
                jumpToPreviousBundleResource
                
            case KeyPressed(_, Key.Down, _, _) =>
                jumpToNextBundleResource
        }
    }
    
    private lazy val buttonPanel: Panel = new FlowPanel {        
        contents += Button("Refresh repositories") {
            progressView.showForOperation("Refreshing repositories") {
                refreshRepositories
            }
        }
        
        contents += installButton
        contents += uninstallButton
        contents += refreshButton
        contents += refreshAllBundlesButton
    }
    
    private lazy val refreshButton: Button = Button("Refresh") {
        refreshSelectedBundleResource
    }
    
    private lazy val installButton: Button = Button("Install") {
        installSelectedBundleResource
    }
    
    private lazy val uninstallButton: Button = Button("Uninstall") {
        uninstallSelectedBundleResource
    }
    
    private lazy val refreshAllBundlesButton = Button("Refresh all bundles") {
        refreshAllBundles
    }
    
    def refreshAllBundles
    
    private var bundleResources: Seq[BundleResource] = Nil
        
    private lazy val progressView: ProgressView = new ProgressView(host = Some(frame), dialogTitle = "Janana")
    
    
    
    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName)
    
    private lazy val frame = new MainFrame {
        title = "Janana Resource Management"
        iconImage = JananaLogo.medium
        preferredSize = new Dimension(800, 500)
        val screenSize = Toolkit.getDefaultToolkit.getScreenSize 
        
        location = new Point(
            (screenSize.getWidth / 2 - preferredSize.getWidth / 2).asInstanceOf[Int], 
            (screenSize.getHeight / 2 - preferredSize.getHeight / 2).asInstanceOf[Int]
        )
        
        contents = new MigPanel("wrap 1", "[grow,fill]", "[fill][grow,fill][fill]") {
            this add bundleResourceTextField
            this add new ScrollPane {
                viewportView = bundleResourceTable
            }
            this add buttonPanel
        }
        override def closeOperation { 
            hide
        }
    }
     
    
    protected def installBundleResource(bundleResource: BundleResource)
        
    protected def findBundleResources(wildcard: String): Seq[BundleResource]
            
    protected def uninstallBundleResource(bundleResource: BundleResource)
    
    protected def refreshBundleResource(bundleResource: BundleResource)
    
    protected def refreshRepositories
    
    /**
     * Refreshes the bundle resource selected in the table.
     */
    def refreshSelectedBundleResource {
        progressView.showForOperation("Refreshing resource") {
            selectedBundleResource.foreach(refreshBundleResource)
        }
    }
    
    /**
     * Uninstalls the bundle resource selected in the table.
     */
    private def uninstallSelectedBundleResource {
        selectedBundleResource.foreach(uninstallBundleResource)
        update
    }
    
    /**
     * Installs the bundle resource selected in the table.
     */
    private def installSelectedBundleResource {
        progressView.showForOperation("Installing resource") {
            selectedBundleResource.foreach(installBundleResource)
            update
        }
    }
    
    /**
     * Shows or hides buttons depending on which bundle resource is selected.
     */
    private def updateButtons {
        selectedBundleResource match {
            case Some(bundleResource) =>
                import bundleResource._
                installButton.visible = !isInstalled
                uninstallButton.visible = isInstalled
                refreshButton.visible = isInstalled
                
            case None =>
                installButton.visible = false
                uninstallButton.visible = false
                refreshButton.visible = false
        }
        buttonPanel.revalidate
    }
    
    /**
     * Does a complete refresh.
     */
    def update {
        Swing.onEDT {
            updateBundleResourceTable
            updateButtons
        }
    }
    
    /**
     * Selects the next bundle resource in the table.
     */
    private def jumpToNextBundleResource {
        val newIndex = selectedIndex match {
            case Some(i) => i + 1
            case None => 0
        }
        tryToSelectRow(newIndex)
        bundleResourceTable.requestFocus
    }
    
    /**
     * Selects the previous bundle resource in the table.
     */
    private def jumpToPreviousBundleResource {
        val newIndex = selectedIndex match {
            case Some(i) => i - 1
            case None => bundleResources.size - 1
        }
        tryToSelectRow(newIndex)
        bundleResourceTable.requestFocus
    }
    
    /**
     * Tries to select the row at the given position in the table.
     */
    private def tryToSelectRow(rowIndex: Int) {
        if (rowIndex < bundleResources.size) {
            bundleResourceTable.selection.rows.clear
            bundleResourceTable.selection.rows += rowIndex
        }
    }
    
    /**
     * Tries to select the given bundle resource in the table.
     */
    private def tryToSelect(bundleResource: BundleResource) {
        val rowIndex = bundleResources.indexOf(bundleResource)
        if (rowIndex >= 0) {
            tryToSelectRow(rowIndex)
        }
    }
    
    
    /**
     * Returns the selected bundle resource
     */
    private def selectedBundleResource = {
        selectedIndex.map(bundleResources(_))
    }
    
    /**
     * Returns the selected index
     */
    private def selectedIndex = {
        val selectedIndices = bundleResourceTable.selection.rows
        if (selectedIndices.size > 0 && selectedIndices.head < bundleResources.size) {
            Some(selectedIndices.head)
        } else {
            None
        }
    }
    
    /**
     * Does the search and refreshes the table content. It tries to keep the current selection.
     */
    private def updateBundleResourceTable {
        val previouslySelectedBundleResource = selectedBundleResource
        bundleResources = findBundleResources(bundleResourceTextField.text)
        bundleResourceTableModel.updateData
        bundleResourceTable.revalidate
        previouslySelectedBundleResource.foreach(tryToSelect)
    }
    
    /**
     * Updates the repository refreshing progress view for showing the given repository.
     */
    def notifyRefreshingRepository(repo: Repository) {
        progressView.updateDescription(repo.getURL.toString)
    }
    
    /**
     * Raises the repository refreshing progress bar to reflect the given progress.
     */
    def updateRepositoryRefreshingProgress(progress: Int) {
        progressView.updateProgress(progress)
    }
    
    def show {
        Swing.onEDT {
            /* Load frame */
            frame
            updateButtons
            updateBundleResourceTable
            bundleResourceTextField.requestFocus
            frame.visible = true
        }
    }
    
    def hide {
        Swing.onEDT {
            frame.visible = false
        }
    }
    
    def dispose {
        Swing.onEDT {
            frame.dispose
        }
    }

}
